package com.xored.glance.ui.controls.tree;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;

public abstract class UnknownTreeVisitor {

	public void visit(IProgressMonitor monitor) {
		monitor.beginTask("Tree indexing", 100);
		try {
			Object[] roots = getRoots();
			monitor.worked(1);
			breadthVisit(roots, monitor, 1);
		} finally {
			monitor.done();
		}
	}

	protected abstract Object[] getRoots();

	protected abstract Object[] getChildren(Object element);

	private void breadthVisit(Object[] elements, IProgressMonitor monitor,
			int level) {
		List<Object> next = new ArrayList<Object>();
		for (Object element : elements) {
			if (monitor.isCanceled())
				return;
			next.addAll(Arrays.asList(getChildren(element)));
		}
		if (monitor.isCanceled())
			return;
		int work = (int) Math.pow(2, level);
		monitor.worked(work);
		int totalWork = 2 * work - 1;
		int size = next.size();
		if (size == 0)
			return;
		int remains = 100 - totalWork;
		if (remains > size) {
			breadthVisit(next.toArray(), monitor, level + 1);
		} else {
			SubProgressMonitor subMonitor = new SubProgressMonitor(monitor,
					remains);
			try {
				subMonitor.beginTask("", size);
				for (Object element : next) {
					depthVisit(element, monitor, level + 1);
					subMonitor.worked(1);
				}
			} finally {
				subMonitor.done();
			}
		}
	}

	private void depthVisit(Object element, IProgressMonitor monitor, int level) {
		if (level > 4) {
			return;
		}
		for (Object child : getChildren(element)) {
			if (monitor.isCanceled())
				return;
			depthVisit(child, monitor, level + 1);
		}
	}

}
